/*
 *              Copyright (C) 2011 The MusicMod Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *            http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.musicmod.android.activity;

import android.app.ExpandableListActivity;
import android.app.SearchManager;
import android.content.AsyncQueryHandler;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.provider.MediaStore;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.SectionIndexer;
import android.widget.SimpleCursorTreeAdapter;
import android.widget.TextView;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;

import java.text.Collator;

import org.musicmod.android.Constants;
import org.musicmod.android.R;
import org.musicmod.android.utils.*;

public class ArtistAlbumTabActivity extends ExpandableListActivity implements
		View.OnCreateContextMenuListener, Constants, ServiceConnection {
	private String mCurrentArtistId;
	private String mCurrentArtistName;
	private String mCurrentAlbumId;
	private String mCurrentAlbumName;
	private String mCurrentArtistNameForAlbum;
	boolean mIsUnknownArtist;
	boolean mIsUnknownAlbum;
	private ArtistAlbumListAdapter mArtistAlbumListAdapter;
	private boolean mAdapterSent;
	private final static int SEARCH = CHILD_MENU_BASE;
	private static int mLastListPosCourse = -1;
	private static int mLastListPosFine = -1;
	private ServiceToken mToken;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);

		requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
		requestWindowFeature(Window.FEATURE_NO_TITLE);

		setVolumeControlStream(AudioManager.STREAM_MUSIC);
		if (icicle != null) {
			mCurrentAlbumId = icicle.getString("selectedalbum");
			mCurrentAlbumName = icicle.getString("selectedalbumname");
			mCurrentArtistId = icicle.getString("selectedartist");
			mCurrentArtistName = icicle.getString("selectedartistname");
		}
		mToken = MusicUtils.bindToService(this, this);

		IntentFilter f = new IntentFilter();
		f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
		f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
		f.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
		f.addDataScheme("file");
		registerReceiver(mScanListener, f);

		setContentView(R.layout.media_picker_activity_expanding);
		MusicUtils.updateButtonBar(this, R.id.artisttab);
		ExpandableListView lv = getExpandableListView();
		lv.setOnCreateContextMenuListener(this);
		lv.setTextFilterEnabled(true);

		mArtistAlbumListAdapter = (ArtistAlbumListAdapter) getLastNonConfigurationInstance();
		if (mArtistAlbumListAdapter == null) {
			// Log.i("@@@", "starting query");
			mArtistAlbumListAdapter = new ArtistAlbumListAdapter(
					getApplication(),
					this,
					null, // cursor
					R.layout.track_list_item_group, new String[] {},
					new int[] {}, R.layout.track_list_item_child,
					new String[] {}, new int[] {});
			setListAdapter(mArtistAlbumListAdapter);
			setTitle(R.string.working_artists);
			getArtistCursor(mArtistAlbumListAdapter.getQueryHandler(), null);
		} else {
			mArtistAlbumListAdapter.setActivity(this);
			setListAdapter(mArtistAlbumListAdapter);
			mArtistCursor = mArtistAlbumListAdapter.getCursor();
			if (mArtistCursor != null) {
				init(mArtistCursor);
			} else {
				getArtistCursor(mArtistAlbumListAdapter.getQueryHandler(), null);
			}
		}
	}

	@Override
	public Object onRetainNonConfigurationInstance() {
		mAdapterSent = true;
		return mArtistAlbumListAdapter;
	}

	@Override
	public void onSaveInstanceState(Bundle outcicle) {
		// need to store the selected item so we don't lose it in case
		// of an orientation switch. Otherwise we could lose it while
		// in the middle of specifying a playlist to add the item to.
		outcicle.putString("selectedalbum", mCurrentAlbumId);
		outcicle.putString("selectedalbumname", mCurrentAlbumName);
		outcicle.putString("selectedartist", mCurrentArtistId);
		outcicle.putString("selectedartistname", mCurrentArtistName);
		super.onSaveInstanceState(outcicle);
	}

	@Override
	public void onDestroy() {
		ExpandableListView lv = getExpandableListView();
		if (lv != null) {
			mLastListPosCourse = lv.getFirstVisiblePosition();
			View cv = lv.getChildAt(0);
			if (cv != null) {
				mLastListPosFine = cv.getTop();
			}
		}

		MusicUtils.unbindFromService(mToken);
		// If we have an mListAdapter and didn't send it off to another activity
		// yet,
		// we should
		// close its cursor, which we do by assigning a null cursor to it. Doing
		// this
		// instead of closing the cursor directly keeps the framework from
		// accessing
		// the closed cursor later.
		if (!mAdapterSent && mArtistAlbumListAdapter != null) {
			// close the cursor if we didn't send it to another activity, to
			// avoid cursor leaks
			mArtistAlbumListAdapter.changeCursor(mArtistAlbumListAdapter
					.getCursor());
		}
		// Because we pass the mListAdapter to the next activity, we need to
		// make
		// sure it doesn't keep a reference to this activity. We can do this
		// by clearing its DatasetObservers, which setListAdapter(null) does.
		setListAdapter(null);
		mArtistAlbumListAdapter = null;
		unregisterReceiver(mScanListener);
		setListAdapter(null);
		super.onDestroy();
	}

	@Override
	public void onResume() {
		super.onResume();
		IntentFilter f = new IntentFilter();
		f.addAction(BROADCAST_META_CHANGED);
		f.addAction(BROADCAST_QUEUE_CHANGED);
		registerReceiver(mTrackListListener, f);
		mTrackListListener.onReceive(null, null);

		MusicUtils.setSpinnerState(this);
	}

	private BroadcastReceiver mTrackListListener = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			getExpandableListView().invalidateViews();
			MusicUtils.updateNowPlaying(ArtistAlbumTabActivity.this);
		}
	};
	private BroadcastReceiver mScanListener = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			MusicUtils.setSpinnerState(ArtistAlbumTabActivity.this);
			mReScanHandler.sendEmptyMessage(0);
			if (intent.getAction().equals(Intent.ACTION_MEDIA_UNMOUNTED)) {
				MusicUtils.clearAlbumArtCache();
			}
		}
	};

	private Handler mReScanHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if (mArtistAlbumListAdapter != null) {
				getArtistCursor(mArtistAlbumListAdapter.getQueryHandler(), null);
			}
		}
	};

	@Override
	public void onPause() {
		unregisterReceiver(mTrackListListener);
		mReScanHandler.removeCallbacksAndMessages(null);
		super.onPause();
	}

	public void init(Cursor c) {

		if (mArtistAlbumListAdapter == null) {
			return;
		}
		mArtistAlbumListAdapter.changeCursor(c); // also sets mArtistCursor

		if (mArtistCursor == null) {
			MusicUtils.displayDatabaseError(this);
			closeContextMenu();
			mReScanHandler.sendEmptyMessageDelayed(0, 1000);
			return;
		}

		// restore previous position
		if (mLastListPosCourse >= 0) {
			ExpandableListView elv = getExpandableListView();
			elv.setSelectionFromTop(mLastListPosCourse, mLastListPosFine);
			mLastListPosCourse = -1;
		}

		MusicUtils.hideDatabaseError(this);
		MusicUtils.updateButtonBar(this, R.id.artisttab);
		setTitle();
	}

	private void setTitle() {
		setTitle(R.string.artists);
	}

	@Override
	public boolean onChildClick(ExpandableListView parent, View v,
			int groupPosition, int childPosition, long id) {

		mCurrentAlbumId = Long.valueOf(id).toString();

		Intent intent = new Intent(Intent.ACTION_PICK);
		intent.setClass(ArtistAlbumTabActivity.this, TrackTabActivity.class);
		intent.putExtra("album", mCurrentAlbumId);
		Cursor c = (Cursor) getExpandableListAdapter().getChild(groupPosition,
				childPosition);
		String album = c.getString(c
				.getColumnIndex(MediaStore.Audio.Albums.ALBUM));
		if (album == null || album.equals(MediaStore.UNKNOWN_STRING)) {
			// unknown album, so we should include the artist ID to limit the
			// songs to songs only by that artist
			mArtistCursor.moveToPosition(groupPosition);
			mCurrentArtistId = mArtistCursor.getString(mArtistCursor
					.getColumnIndex(MediaStore.Audio.Artists._ID));
			intent.putExtra("artist", mCurrentArtistId);
		}
		startActivity(intent);
		return true;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {

		MenuInflater mInflater = getMenuInflater();
		mInflater.inflate(R.menu.options_menu_artist, menu);

		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		MusicUtils.setPartyShuffleMenuIcon(menu);
		return super.onPrepareOptionsMenu(menu);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		Intent intent;
		Cursor cursor;
		switch (item.getItemId()) {
		case PARTY_SHUFFLE:
			MusicUtils.togglePartyShuffle();
			break;

		case SHUFFLE_ALL:
			cursor = MusicUtils.query(this,
					MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
					new String[] { MediaStore.Audio.Media._ID },
					MediaStore.Audio.Media.IS_MUSIC + "=1", null,
					MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
			if (cursor != null) {
				MusicUtils.shuffleAll(this, cursor);
				cursor.close();
			}
			return true;

		case SETTINGS:
			intent = new Intent();
			intent.setAction(INTENT_BASE_CONFIG);
			startActivityForResult(intent, SETTINGS);
			return true;
		}
		return super.onOptionsItemSelected(item);
	}

	@Override
	public void onCreateContextMenu(ContextMenu menu, View view,
			ContextMenuInfo menuInfoIn) {
		menu.add(0, PLAY_SELECTION, 0, R.string.play_selection);
		SubMenu sub = menu.addSubMenu(0, ADD_TO_PLAYLIST, 0,
				R.string.add_to_playlist);
		MusicUtils.makePlaylistMenu(this, sub);
		menu.add(0, DELETE_ITEMS, 0, R.string.delete);

		ExpandableListContextMenuInfo mi = (ExpandableListContextMenuInfo) menuInfoIn;

		int itemtype = ExpandableListView
				.getPackedPositionType(mi.packedPosition);
		int gpos = ExpandableListView.getPackedPositionGroup(mi.packedPosition);
		int cpos = ExpandableListView.getPackedPositionChild(mi.packedPosition);
		if (itemtype == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
			if (gpos == -1) {
				// this shouldn't happen
				Log.d("Artist/Album", "no group");
				return;
			}
			gpos = gpos - getExpandableListView().getHeaderViewsCount();
			mArtistCursor.moveToPosition(gpos);
			mCurrentArtistId = mArtistCursor.getString(mArtistCursor
					.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID));
			mCurrentArtistName = mArtistCursor.getString(mArtistCursor
					.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
			mCurrentAlbumId = null;
			mIsUnknownArtist = mCurrentArtistName == null
					|| mCurrentArtistName.equals(MediaStore.UNKNOWN_STRING);
			mIsUnknownAlbum = true;
			if (mIsUnknownArtist) {
				menu.setHeaderTitle(getString(R.string.unknown_artist_name));
			} else {
				menu.setHeaderTitle(mCurrentArtistName);
				menu.add(0, SEARCH, 0, android.R.string.search_go);
			}
			return;
		} else if (itemtype == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
			if (cpos == -1) {
				// this shouldn't happen
				Log.d("Artist/Album", "no child");
				return;
			}
			Cursor c = (Cursor) getExpandableListAdapter().getChild(gpos, cpos);
			c.moveToPosition(cpos);
			mCurrentArtistId = null;
			mCurrentAlbumId = Long.valueOf(mi.id).toString();
			mCurrentAlbumName = c.getString(c
					.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM));
			gpos = gpos - getExpandableListView().getHeaderViewsCount();
			mArtistCursor.moveToPosition(gpos);
			mCurrentArtistNameForAlbum = mArtistCursor.getString(mArtistCursor
					.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
			mIsUnknownArtist = mCurrentArtistNameForAlbum == null
					|| mCurrentArtistNameForAlbum
							.equals(MediaStore.UNKNOWN_STRING);
			mIsUnknownAlbum = mCurrentAlbumName == null
					|| mCurrentAlbumName.equals(MediaStore.UNKNOWN_STRING);
			if (mIsUnknownAlbum) {
				menu.setHeaderTitle(getString(R.string.unknown_album_name));
			} else {
				menu.setHeaderTitle(mCurrentAlbumName);
			}
			if (!mIsUnknownAlbum || !mIsUnknownArtist) {
				menu.add(0, SEARCH, 0, android.R.string.search_go);
			}
		}
	}

	@Override
	public boolean onContextItemSelected(MenuItem item) {
		Intent intent;

		switch (item.getItemId()) {
		case PLAY_SELECTION: {
			// play everything by the selected artist
			long[] list = mCurrentArtistId != null ? MusicUtils
					.getSongListForArtist(this,
							Long.parseLong(mCurrentArtistId)) : MusicUtils
					.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));

			MusicUtils.playAll(this, list, 0);
			return true;
		}

		case QUEUE: {
			long[] list = mCurrentArtistId != null ? MusicUtils
					.getSongListForArtist(this,
							Long.parseLong(mCurrentArtistId)) : MusicUtils
					.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
			MusicUtils.addToCurrentPlaylist(this, list);
			return true;
		}

		case NEW_PLAYLIST: {
			intent = new Intent();
			intent.setAction(INTENT_CREATE_PLAYLIST);
			startActivityForResult(intent, NEW_PLAYLIST);
			return true;
		}

		case PLAYLIST_SELECTED: {
			long[] list = mCurrentArtistId != null ? MusicUtils
					.getSongListForArtist(this,
							Long.parseLong(mCurrentArtistId)) : MusicUtils
					.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
			long playlist = item.getIntent().getLongExtra("playlist", 0);
			MusicUtils.addToPlaylist(this, list, playlist);
			return true;
		}

		case DELETE_ITEMS: {
			final long[] list;
			String content;
			Bundle bundle = new Bundle();
			if (mCurrentArtistId != null) {
				list = MusicUtils.getSongListForArtist(this,
						Long.parseLong(mCurrentArtistId));
				content = mCurrentArtistName;
				bundle.putString(KEY_TYPE, TYPE_ARTIST_ALBUM);
			} else {
				list = MusicUtils.getSongListForAlbum(this,
						Long.parseLong(mCurrentAlbumId));
				content = mCurrentAlbumName;
				bundle.putString(KEY_TYPE, TYPE_ALBUM);
			}
			bundle.putString(KEY_CONTENT, content);
			bundle.putLongArray("items", list);
			intent = new Intent();
			intent.setAction(INTENT_DELETE_ITEMS);
			intent.putExtras(bundle);
			startActivity(intent);
			return true;
		}

		case SEARCH:
			doSearch();
			return true;
		}
		return super.onContextItemSelected(item);
	}

	void doSearch() {
		CharSequence title = null;
		String query = null;

		Intent i = new Intent();
		i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
		i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

		if (mCurrentArtistId != null) {
			title = mCurrentArtistName;
			query = mCurrentArtistName;
			i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistName);
			i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS,
					MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE);
		} else {
			if (mIsUnknownAlbum) {
				title = query = mCurrentArtistNameForAlbum;
			} else {
				title = query = mCurrentAlbumName;
				if (!mIsUnknownArtist) {
					query = query + " " + mCurrentArtistNameForAlbum;
				}
			}
			i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST,
					mCurrentArtistNameForAlbum);
			i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, mCurrentAlbumName);
			i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS,
					MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE);
		}
		title = getString(R.string.mediasearch, title);
		i.putExtra(SearchManager.QUERY, query);

		startActivity(Intent.createChooser(i, title));
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode,
			Intent intent) {
		switch (requestCode) {
		case SCAN_DONE:
			if (resultCode == RESULT_CANCELED) {
				finish();
			} else {
				getArtistCursor(mArtistAlbumListAdapter.getQueryHandler(), null);
			}
			break;

		case NEW_PLAYLIST:
			if (resultCode == RESULT_OK) {
				Uri uri = intent.getData();
				if (uri != null) {
					long[] list = null;
					if (mCurrentArtistId != null) {
						list = MusicUtils.getSongListForArtist(this,
								Long.parseLong(mCurrentArtistId));
					} else if (mCurrentAlbumId != null) {
						list = MusicUtils.getSongListForAlbum(this,
								Long.parseLong(mCurrentAlbumId));
					}
					MusicUtils.addToPlaylist(this, list,
							Long.parseLong(uri.getLastPathSegment()));
				}
			}
			break;
		}
	}

	private Cursor getArtistCursor(AsyncQueryHandler async, String filter) {

		StringBuilder where = new StringBuilder();
		where.append(MediaStore.Audio.Artists.ARTIST + " != ''");

		// Add in the filtering constraints
		String[] keywords = null;
		if (filter != null) {
			String[] searchWords = filter.split(" ");
			keywords = new String[searchWords.length];
			Collator col = Collator.getInstance();
			col.setStrength(Collator.PRIMARY);
			for (int i = 0; i < searchWords.length; i++) {
				String key = MediaStore.Audio.keyFor(searchWords[i]);
				key = key.replace("\\", "\\\\");
				key = key.replace("%", "\\%");
				key = key.replace("_", "\\_");
				keywords[i] = '%' + key + '%';
			}
			for (int i = 0; i < searchWords.length; i++) {
				where.append(" AND ");
				where.append(MediaStore.Audio.Media.ARTIST_KEY
						+ " LIKE ? ESCAPE '\\'");
			}
		}

		String whereclause = where.toString();
		String[] cols = new String[] { MediaStore.Audio.Artists._ID,
				MediaStore.Audio.Artists.ARTIST,
				MediaStore.Audio.Artists.NUMBER_OF_ALBUMS,
				MediaStore.Audio.Artists.NUMBER_OF_TRACKS };
		Cursor ret = null;
		if (async != null) {
			async.startQuery(0, null,
					MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, cols,
					whereclause, keywords, MediaStore.Audio.Artists.ARTIST_KEY);
		} else {
			ret = MusicUtils.query(this,
					MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, cols,
					whereclause, keywords, MediaStore.Audio.Artists.ARTIST_KEY);
		}
		return ret;
	}

	static class ArtistAlbumListAdapter extends SimpleCursorTreeAdapter
			implements SectionIndexer {

		private final Drawable mNowPlayingOverlay;
		private final BitmapDrawable mDefaultAlbumIcon;
		private int mGroupArtistIdIdx;
		private int mGroupArtistIdx;
		private int mGroupAlbumIdx;
		private int mGroupSongIdx;
		private final Resources mResources;
		private final String mUnknownAlbum;
		private final String mUnknownArtist;
		private final StringBuilder mBuffer = new StringBuilder();
		private final Object[] mFormatArgs = new Object[1];
		private final Object[] mFormatArgs3 = new Object[3];
		private MusicAlphabetIndexer mIndexer;
		private ArtistAlbumTabActivity mActivity;
		private AsyncQueryHandler mQueryHandler;
		private String mConstraint = null;
		private boolean mConstraintIsValid = false;

		static class ViewHolder {
			TextView line1;
			TextView line2;
			ImageView play_indicator;
			ImageView icon;
		}

		class QueryHandler extends AsyncQueryHandler {
			QueryHandler(ContentResolver res) {
				super(res);
			}

			@Override
			protected void onQueryComplete(int token, Object cookie,
					Cursor cursor) {
				// Log.i("@@@", "query complete");
				mActivity.init(cursor);
			}
		}

		ArtistAlbumListAdapter(Context context,
				ArtistAlbumTabActivity currentactivity, Cursor cursor,
				int glayout, String[] gfrom, int[] gto, int clayout,
				String[] cfrom, int[] cto) {
			super(context, cursor, glayout, gfrom, gto, clayout, cfrom, cto);
			mActivity = currentactivity;
			mQueryHandler = new QueryHandler(context.getContentResolver());

			Resources r = context.getResources();
			mNowPlayingOverlay = r
					.getDrawable(R.drawable.indicator_ic_mp_playing_list);
			mDefaultAlbumIcon = (BitmapDrawable) r
					.getDrawable(R.drawable.ic_mp_album_list);
			// no filter or dither, it's a lot faster and we can't tell the
			// difference
			mDefaultAlbumIcon.setFilterBitmap(false);
			mDefaultAlbumIcon.setDither(false);

			getColumnIndices(cursor);
			mResources = context.getResources();
			mUnknownAlbum = context.getString(R.string.unknown_album_name);
			mUnknownArtist = context.getString(R.string.unknown_artist_name);
		}

		private void getColumnIndices(Cursor cursor) {
			if (cursor != null) {
				mGroupArtistIdIdx = cursor
						.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID);
				mGroupArtistIdx = cursor
						.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST);
				mGroupAlbumIdx = cursor
						.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_ALBUMS);
				mGroupSongIdx = cursor
						.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_TRACKS);
				if (mIndexer != null) {
					mIndexer.setCursor(cursor);
				} else {
					mIndexer = new MusicAlphabetIndexer(cursor,
							mGroupArtistIdx,
							mResources.getString(R.string.fast_scroll_alphabet));
				}
			}
		}

		public void setActivity(ArtistAlbumTabActivity newactivity) {
			mActivity = newactivity;
		}

		public AsyncQueryHandler getQueryHandler() {
			return mQueryHandler;
		}

		@Override
		public View newGroupView(Context context, Cursor cursor,
				boolean isExpanded, ViewGroup parent) {
			View v = super.newGroupView(context, cursor, isExpanded, parent);
			ImageView iv = (ImageView) v.findViewById(R.id.icon);
			ViewGroup.LayoutParams p = iv.getLayoutParams();
			p.width = ViewGroup.LayoutParams.WRAP_CONTENT;
			p.height = ViewGroup.LayoutParams.WRAP_CONTENT;
			ViewHolder vh = new ViewHolder();
			vh.line1 = (TextView) v.findViewById(R.id.line1);
			vh.line2 = (TextView) v.findViewById(R.id.line2);
			vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator);
			vh.icon = (ImageView) v.findViewById(R.id.icon);
			vh.icon.setPadding(0, 0, 1, 0);
			v.setTag(vh);
			return v;
		}

		@Override
		public View newChildView(Context context, Cursor cursor,
				boolean isLastChild, ViewGroup parent) {
			View v = super.newChildView(context, cursor, isLastChild, parent);
			ViewHolder vh = new ViewHolder();
			vh.line1 = (TextView) v.findViewById(R.id.line1);
			vh.line2 = (TextView) v.findViewById(R.id.line2);
			vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator);
			vh.icon = (ImageView) v.findViewById(R.id.icon);
			vh.icon.setBackgroundDrawable(mDefaultAlbumIcon);
			vh.icon.setPadding(0, 0, 1, 0);
			v.setTag(vh);
			return v;
		}

		@Override
		public void bindGroupView(View view, Context context, Cursor cursor,
				boolean isexpanded) {

			ViewHolder vh = (ViewHolder) view.getTag();

			String artist = cursor.getString(mGroupArtistIdx);
			String displayartist = artist;
			boolean unknown = artist == null
					|| artist.equals(MediaStore.UNKNOWN_STRING);
			if (unknown) {
				displayartist = mUnknownArtist;
			}
			vh.line1.setText(displayartist);

			int numalbums = cursor.getInt(mGroupAlbumIdx);
			int numsongs = cursor.getInt(mGroupSongIdx);

			String songs_albums = MusicUtils.makeAlbumsLabel(context,
					numalbums, numsongs, unknown);

			vh.line2.setText(songs_albums);

			long currentartistid = MusicUtils.getCurrentArtistId();
			long artistid = cursor.getLong(mGroupArtistIdIdx);
			if (currentartistid == artistid && !isexpanded) {
				vh.play_indicator.setImageDrawable(mNowPlayingOverlay);
			} else {
				vh.play_indicator.setImageDrawable(null);
			}
		}

		@Override
		public void bindChildView(View view, Context context, Cursor cursor,
				boolean islast) {

			ViewHolder vh = (ViewHolder) view.getTag();

			String name = cursor.getString(cursor
					.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM));
			String displayname = name;
			boolean unknown = name == null
					|| name.equals(MediaStore.UNKNOWN_STRING);
			if (unknown) {
				displayname = mUnknownAlbum;
			}
			vh.line1.setText(displayname);

			int numsongs = cursor
					.getInt(cursor
							.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS));
			int numartistsongs = cursor
					.getInt(cursor
							.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST));

			final StringBuilder builder = mBuffer;
			builder.delete(0, builder.length());
			if (unknown) {
				numsongs = numartistsongs;
			}

			if (numsongs == numartistsongs) {
				final Object[] args = mFormatArgs;
				args[0] = numsongs;
				builder.append(mResources.getQuantityString(R.plurals.Nsongs,
						numsongs, args));
			} else {
				final Object[] args = mFormatArgs3;
				args[0] = numsongs;
				args[1] = numartistsongs;
				args[2] = cursor
						.getString(cursor
								.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
				builder.append(mResources.getQuantityString(
						R.plurals.Nsongscomp, numsongs, args));
			}
			vh.line2.setText(builder.toString());

			ImageView iv = vh.icon;
			// We don't actually need the path to the thumbnail file,
			// we just use it to see if there is album art or not
			String art = cursor.getString(cursor
					.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM_ART));
			if (unknown || art == null || art.length() == 0) {
				iv.setBackgroundDrawable(mDefaultAlbumIcon);
				iv.setImageDrawable(null);
			} else {
				long artIndex = cursor.getLong(0);
				Drawable d = MusicUtils.getCachedArtwork(context, artIndex,
						mDefaultAlbumIcon);
				iv.setImageDrawable(d);
			}

			long currentalbumid = MusicUtils.getCurrentAlbumId();
			long aid = cursor.getLong(0);
			iv = vh.play_indicator;
			if (currentalbumid == aid) {
				iv.setImageDrawable(mNowPlayingOverlay);
			} else {
				iv.setImageDrawable(null);
			}
		}

		@Override
		protected Cursor getChildrenCursor(Cursor groupCursor) {

			long id = groupCursor.getLong(groupCursor
					.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID));

			String[] cols = new String[] { MediaStore.Audio.Albums._ID,
					MediaStore.Audio.Albums.ALBUM,
					MediaStore.Audio.Albums.NUMBER_OF_SONGS,
					MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
					MediaStore.Audio.Albums.ALBUM_ART };
			Cursor c = MusicUtils.query(mActivity,
					MediaStore.Audio.Artists.Albums.getContentUri("external",
							id), cols, null, null,
					MediaStore.Audio.Albums.DEFAULT_SORT_ORDER);

			class MyCursorWrapper extends CursorWrapper {
				String mArtistName;
				int mMagicColumnIdx;

				MyCursorWrapper(Cursor c, String artist) {
					super(c);
					mArtistName = artist;
					if (mArtistName == null
							|| mArtistName.equals(MediaStore.UNKNOWN_STRING)) {
						mArtistName = mUnknownArtist;
					}
					mMagicColumnIdx = c.getColumnCount();
				}

				@Override
				public String getString(int columnIndex) {
					if (columnIndex != mMagicColumnIdx) {
						return super.getString(columnIndex);
					}
					return mArtistName;
				}

				@Override
				public int getColumnIndexOrThrow(String name) {
					if (MediaStore.Audio.Albums.ARTIST.equals(name)) {
						return mMagicColumnIdx;
					}
					return super.getColumnIndexOrThrow(name);
				}

				@Override
				public String getColumnName(int idx) {
					if (idx != mMagicColumnIdx) {
						return super.getColumnName(idx);
					}
					return MediaStore.Audio.Albums.ARTIST;
				}

				@Override
				public int getColumnCount() {
					return super.getColumnCount() + 1;
				}
			}
			return new MyCursorWrapper(c,
					groupCursor.getString(mGroupArtistIdx));
		}

		@Override
		public void changeCursor(Cursor cursor) {
			if (mActivity.isFinishing() && cursor != null) {
				cursor.close();
				cursor = null;
			}
			if (cursor != mActivity.mArtistCursor) {
				mActivity.mArtistCursor = cursor;
				getColumnIndices(cursor);
				super.changeCursor(cursor);
			}
		}

		@Override
		public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
			String s = constraint.toString();
			if (mConstraintIsValid
					&& ((s == null && mConstraint == null) || (s != null && s
							.equals(mConstraint)))) {
				return getCursor();
			}
			Cursor c = mActivity.getArtistCursor(null, s);
			mConstraint = s;
			mConstraintIsValid = true;
			return c;
		}

		public Object[] getSections() {
			return mIndexer.getSections();
		}

		public int getPositionForSection(int sectionIndex) {
			return mIndexer.getPositionForSection(sectionIndex);
		}

		public int getSectionForPosition(int position) {
			return 0;
		}
	}

	private Cursor mArtistCursor;

	public void onServiceConnected(ComponentName name, IBinder service) {
		MusicUtils.updateNowPlaying(this);
	}

	public void onServiceDisconnected(ComponentName name) {
		finish();
	}
}
